package com.dynamic.mybatis.web.controller;


import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.dynamic.mybatis.core.enums.Alert;
import com.dynamic.mybatis.core.metadata.DynamicMappedStatement;
import com.dynamic.mybatis.core.metadata.DynamicMappedStatementMixedResult;
import com.dynamic.mybatis.core.metadata.DynamicPage;
import com.dynamic.mybatis.core.metadata.MappedStatementMetaField;
import com.dynamic.mybatis.core.mock.Mock;
import com.dynamic.mybatis.core.parser.xml.DynamicMappedStatementParser;
import com.dynamic.mybatis.core.session.DynamicSqlSessionTemplate;
import com.dynamic.mybatis.core.utils.*;
import com.dynamic.mybatis.web.util.HtmlTemplateUtils;
import com.dynamic.mybatis.web.vo.DynamicMappedStatementMockVo;
import com.dynamic.mybatis.web.vo.DynamicMappedStatementVo;
import com.dynamic.mybatis.web.vo.MappedStatementQueryVo;
import com.dynamic.mybatis.web.vo.MappedStatementValidateVo;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RequestMapping("/dms/")
@RestController
public class MappedStatementController {
    Logger logger = LoggerFactory.getLogger(MappedStatementController.class);

    @Autowired
    private DynamicSqlSessionTemplate sqlSessionTemplate;


    @PostMapping("/generateParser")
    public DynamicMappedStatementMixedResult generateParser(@RequestBody MappedStatementValidateVo validate) {
        DynamicMappedStatementMixedResult result = null;
        DynamicMappedStatementParser mappedStatementParser = sqlSessionTemplate.getDynamicMappedStatementHelper().getMappedStatementParser();
        //自定义SQL
        if (StringUtils.isNotBlank(validate.getSqlStatement())) {
            result = mappedStatementParser.validate(validate.getNamespace(), validate.getDataSourceCodes(), validate.getSqlStatement(), validate.getParses());
            //单表
        } else if (CollectionUtils.isNotEmpty(validate.getTables())) {
            result = mappedStatementParser.validate(validate.getNamespace(), validate.getDataSourceCodes(), validate.getTables(), validate.getMethods(), validate.getParses());
        }
        //处理已经合并的数据
        DynamicMappedStatementMixedResult result1 = mappedStatementParser.merge(validate.getNamespace(), result.getSqlStatement(), BeanUtils.serializeCopy(result.getParserResults()));
        return resultHandle(result, result1, validate.getOperate());
    }

    @PostMapping("/test")
    public DynamicMappedStatementMixedResult test(@RequestBody MappedStatementValidateVo validate) {
        //校验
        DynamicMappedStatementMixedResult result = generateParser(BeanUtils.serializeCopy(validate));
        DynamicMappedStatementMixedResult result1 = null;
        if (result.getStatus() != null && result.getStatus() != Alert.StandardAlert.error) {
            //测试执行
            DynamicMappedStatementParser mappedStatementParser = sqlSessionTemplate.getDynamicMappedStatementHelper().getMappedStatementParser();
            result1 = mappedStatementParser.parseValidateExecute(Boolean.TRUE, validate.getNamespace(), validate.getSqlStatement(), BeanUtils.serializeCopy(result.getParserResults()));
        }
        if (result1 != null && result != null) {
            result1.setParserResults(result.getParserResults());
        }
        return resultHandle(result, result1, validate.getOperate());
    }

    @PostMapping("/submit")
    public DynamicMappedStatementMixedResult submit(@RequestBody MappedStatementValidateVo validate) {
        DynamicMappedStatementParser mappedStatementParser = sqlSessionTemplate.getDynamicMappedStatementHelper().getMappedStatementParser();
        //测试 序列化深拷贝
        DynamicMappedStatementMixedResult result = test(BeanUtils.serializeCopy(validate));
        DynamicMappedStatementMixedResult result1 = null;
        //测试成功处理
        if (result.getStatus() != null && result.getStatus() != Alert.StandardAlert.error) {
            //解析序列化深拷贝
            result1 = mappedStatementParser.parseValidateExecute(Boolean.FALSE, validate.getNamespace(), validate.getSqlStatement(), BeanUtils.serializeCopy(result.getParserResults()));
        }
        return resultHandle(result, result1, validate.getOperate());
    }


    private DynamicMappedStatementMixedResult resultHandle(DynamicMappedStatementMixedResult result, DynamicMappedStatementMixedResult result1, String operate) {
        if (result == null) {
            result = new DynamicMappedStatementMixedResult();
        } else if (result1 != null) {
            if (result1.getStatus() == Alert.StandardAlert.success) {
                result1.setStatus(result.getStatus());
                result1.setMessage(result.getMessage());
            }
            result = result1;
        }
        result.setOperate(operate);
        return result;
    }

    @RequestMapping("/page")
    public DynamicPage page(MappedStatementQueryVo queryVo) {
        List<DynamicMappedStatement> dmsList = filter(queryVo).skip((queryVo.getPageNumber() - 1) * queryVo.getPageSize()).limit(queryVo.getPageSize()).collect(Collectors.toList());
        List<DynamicMappedStatementVo> dmsVoList = pageTransport(dmsList);
        TreeUtil.setTree(dmsVoList, 0, "treeParentId", "treeId", "children");
        dmsVoList = TreeUtil.toArray(dmsVoList, "children");
        long total = filter(queryVo).count();
        return queryVo.result(dmsVoList, total);
    }

    private List<DynamicMappedStatementVo> pageTransport(List<DynamicMappedStatement> dmsList) {
        return dmsList.stream().map(d -> {
            DynamicMappedStatementVo dmsVo = new DynamicMappedStatementVo();
            BeanUtils.copy(d, dmsVo);
            if (CollectionUtils.isNotEmpty(d.getChildren())) {
                List<DynamicMappedStatement> children = dmsVo.getChildren();
                dmsVo.setChildren(new ArrayList<>());
                dmsVo.getChildren().addAll(pageTransport(children));
            }
            return dmsVo;
        }).collect(Collectors.toList());
    }


    @RequestMapping("/getById")
    public DynamicMappedStatementMixedResult getById(String id) {
        return sqlSessionTemplate.getDynamicMappedStatementHelper().getMappedStatementParser().parseMappedStatement(id);
    }

    @PostMapping("print")
    public void print11(@RequestBody(required = false) MappedStatementQueryVo queryVo, HttpServletResponse response) throws IOException {
        //static/pages/dms/api/api.html 需要将该页面引入的非私有样式代码或样式文件内容拷贝到apiTemplate.html中
        File apiHtmlTemplateFile = ResourceUtils.getFile("classpath:static/pages/dms/api/apiTemplate.html");
        String htmlTemplate = FileUtils.read(apiHtmlTemplateFile);
        Document doc = Jsoup.parse(htmlTemplate);
        Element e = doc.selectFirst("body");
        List<DynamicMappedStatement> dms = filter(queryVo).collect(Collectors.toList());
        String bodyHtml = HtmlTemplateUtils.htmlBuilder(dms);
        e.append(bodyHtml);

        //  FileUtils.write(doc.outerHtml(),new File("D:\\test123.html"));

        ByteArrayOutputStream outStream = new ByteArrayOutputStream();

        W3CDom w3cDom = new W3CDom();
        org.w3c.dom.Document w3cDoc = w3cDom.fromJsoup(doc);

        PdfRendererBuilder pdfBuilder = new PdfRendererBuilder();
        pdfBuilder.useFastMode();
        pdfBuilder.withW3cDocument(w3cDoc, "/");
        //中文字体
        File simkaiFontFile = ResourceUtils.getFile("classpath:static/pages/dms/api/simkai.ttf");
        pdfBuilder.useFont(simkaiFontFile, "simkai");

        //css中字体图标
        File bsttfontFontFile = ResourceUtils.getFile("classpath:static/plugs/bootstrap-treetable/bstt-font.ttf");
        pdfBuilder.useFont(bsttfontFontFile, "bstt-font");


        pdfBuilder.useDefaultPageSize(15f, BaseRendererBuilder.PAGE_SIZE_LETTER_HEIGHT, BaseRendererBuilder.PAGE_SIZE_LETTER_UNITS);
        pdfBuilder.toStream(outStream);
        pdfBuilder.run();

        byte[] bytes = outStream.toByteArray();

        response.addHeader("Content-Disposition", "attachment;filename=" + DateUtils.format(new Date(), "yyyyMMddHHssmm") + "_dms.pdf");
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Length", "" + bytes.length);

        response.getOutputStream().write(bytes);
        outStream.close();
        response.flushBuffer();
    }


    @RequestMapping("/mock")
    public DynamicMappedStatementMockVo mock(String id) {
        DynamicMappedStatement dms = sqlSessionTemplate.getDynamicMappedStatementHelper().getDynamicMappedStatement(id);
        DynamicMappedStatementMockVo mockVo = new DynamicMappedStatementMockVo();
        if (dms == null) {
            return mockVo;
        }
        Object headers = Mock.mock(dms.getRequestHeader().get(0));
        Object inputParam = Mock.mock(dms.getInputParameter().get(0));
        Object outParam = Mock.mock(dms.getOutputParameter().get(0));
        mockVo.setInputHead(headers);
        mockVo.setInputParameter(inputParam);
        mockVo.setOutParameter(outParam);
        return mockVo;
    }

    @RequestMapping("/query")
    public List<DynamicMappedStatement> query(@RequestBody MappedStatementQueryVo queryVo) {
        List<DynamicMappedStatement> dms = filter(queryVo).collect(Collectors.toList());
        if (queryVo.isExpand()) {
            dms = sqlSessionTemplate.getDynamicMappedStatementHelper().getMappedStatementParser().queryExpandMappedStatement(dms);
        }
        return transformDynamicMappedStatement(dms);
    }

    @PostMapping("/export")
    public void export(HttpServletResponse response, @RequestBody MappedStatementQueryVo queryVo) {
        Stream<DynamicMappedStatement> stream = filter(queryVo);

        try (OutputStream outputStream = response.getOutputStream();) {
            byte[] bytes = BeanUtils.serialize(stream.collect(Collectors.toList()));
            response.addHeader("Content-Disposition", "attachment;filename=" + DateUtils.format(new Date(), "yyyyMMddHHssmm") + ".dms");
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Length", "" + bytes.length);
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @PostMapping("/import")
    public void importDms(@RequestParam("file") MultipartFile file) {

        List<DynamicMappedStatement> list;
        try {
            list = BeanUtils.deserialize(file.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("文件已损坏");
        }
        sqlSessionTemplate.getDynamicMappedStatementHelper().addDynamicMappedStatement(list);
    }

    private List<DynamicMappedStatement> transformDynamicMappedStatement(List<DynamicMappedStatement> dms) {
        for (DynamicMappedStatement d : dms) {
            transformMappedStatementMetaField(d.getInputParameter());
            transformMappedStatementMetaField(d.getOutputParameter());
        }
        return dms;
    }

    private void transformMappedStatementMetaField(List<MappedStatementMetaField> fields) {
        for (MappedStatementMetaField field : fields) {
            String remarks = field.getRemarks();
            if (field.getValidator() != null && CollectionUtils.isNotEmpty(field.getValidator().getEnums())) {
                String enums = field.getValidator().getEnums().entrySet().stream().map(entry -> entry.getKey() + " = " + Objects.toString(entry.getValue(), "")).collect(Collectors.joining(System.lineSeparator()));
                field.setRemarks(Objects.toString(remarks, "") + enums);
            }
            if (CollectionUtils.isNotEmpty(field.getChildren())) {
                transformMappedStatementMetaField(field.getChildren());
            }
        }
    }

    private Stream<DynamicMappedStatement> filter(MappedStatementQueryVo queryVo) {
        return filter(sqlSessionTemplate.getDynamicMappedStatementHelper().mappedStatementValues(), true, queryVo);
    }

    private Stream<DynamicMappedStatement> filter(Collection<DynamicMappedStatement> dmsList, boolean rootNode, MappedStatementQueryVo queryVo) {

        return dmsList.stream().filter(d -> {
            if (!(d instanceof DynamicMappedStatement)) {
                return Boolean.FALSE;
            }
            boolean flag = Boolean.TRUE;
            if (rootNode && !StringUtils.isBlank(d.getParentId())) {
                flag = Boolean.FALSE;
            }
            if (StringUtils.isNotBlank(queryVo.getId()) && !Objects.equals(d.getId(), queryVo.getId())) {
                flag = Boolean.FALSE;
            } else if (StringUtils.isNotBlank(queryVo.getNamespace()) && !Objects.equals(d.getNamespace(), queryVo.getNamespace())) {
                flag = Boolean.FALSE;
            } else if (StringUtils.isNotBlank(queryVo.getShortId()) && !Objects.equals(d.getShortId(), queryVo.getShortId())) {
                flag = Boolean.FALSE;
            } else if (StringUtils.isNotBlank(queryVo.getCommandType()) && !Objects.equals(d.getCommandType().commandType(), queryVo.getCommandType())) {
                flag = Boolean.FALSE;
            } else if (StringUtils.isNotBlank(queryVo.getReturnType()) && !Objects.equals(d.getReturnType().returnType(), queryVo.getReturnType())) {
                flag = Boolean.FALSE;
            } else if (StringUtils.isNotBlank(queryVo.getMethod()) && !Objects.equals(d.getMethod().method(), queryVo.getMethod())) {
                flag = Boolean.FALSE;
            } else if (CollectionUtils.isNotEmpty(queryVo.getUids()) && queryVo.getUids().indexOf(d.getUid()) < 0) {
                flag = Boolean.FALSE;
            } else if (StringUtils.isNotEmpty(queryVo.getTableName()) && (CollectionUtils.isEmpty(d.getTables()) || !d.getTables().contains(queryVo.getTableName()))) {
                flag = Boolean.FALSE;
            }
            if (!flag && CollectionUtils.isNotEmpty(d.getChildren())) {
                flag = filter(d.getChildren(), false, queryVo).findFirst().isPresent();
            }
            return flag;
        });
    }
}